﻿function NDateTimeFromJsObject(jsDateObject) {
    return new NDateTime(null, null, null, null, null, null, null, jsDateObject);
}

function NDateTime(year, month, day, hours, minutes, seconds, milliseconds, jsDateObject) {
    if (jsDateObject != undefined)
        this.jsdateObject = jsDateObject;
    else {
        hours = typeof hours !== 'undefined' ? hours : 0;
        minutes = typeof minutes !== 'undefined' ? minutes : 0;
        seconds = typeof seconds !== 'undefined' ? seconds : 0;
        milliseconds = typeof milliseconds !== 'undefined' ? milliseconds : 0;
        this.jsdateObject = new Date(year, month - 1, day, hours, minutes, seconds, milliseconds);
    }
    this._day = this.jsdateObject.getDate();
    this._month = this.jsdateObject.getMonth() + 1;
    this._year = this.jsdateObject.getFullYear();
    this._hour = this.jsdateObject.getHours();
    this._minute = this.jsdateObject.getMinutes();
    this._second = this.jsdateObject.getSeconds();
    this._millisecond = this.jsdateObject.getMilliseconds();
    this._date = new Date(this.jsdateObject.getFullYear(), this.jsdateObject.getMonth(), this.jsdateObject.getDate(), 0, 0, 0, 0);
    //this._time = this.jsdateObject.toLocaleTimeString();
    this._dayOfWeek = this.jsdateObject.getDay();
    this._javaScriptDateObject = this.jsdateObject;
}

//Properties
//-------------------------------------------------
Object.defineProperty(NDateTime.prototype, "Day", {
    get: function() {
        return this._day;
    },
    set: function (value) {
        throw "The property 'Day' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "Month", {
    get: function () {
        return this._month;
    },
    set: function (value) {
        throw "The property 'Month' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "Year", {
    get: function () {
        return this._year;
    },
    set: function (value) {
        throw "The property 'Year' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "Hour", {
    get: function () {
        return this._hour;
    },
    set: function (value) {
        throw "The property 'Hour' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "Minute", {
    get: function () {
        return this._minute;
    },
    set: function (value) {
        throw "The property 'Minute' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "Second", {
    get: function () {
        return this._second;
    },
    set: function (value) {
        throw "The property 'Second' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "Millisecond", {
    get: function () {
        return this._millisecond;
    },
    set: function (value) {
        throw "The property 'Millisecond' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "Date", {
    get: function () {
        return new NDateTime(null, null, null, null, null, null, null, this._date);
    },
    set: function (value) {
        throw "The property 'Date' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "DayOfWeek", {
    get: function () {
        return this._dayOfWeek;
    },
    set: function (value) {
        throw "The property 'DayOfWeek' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "JavaScriptDateObject", {
    get: function () {
        return this._javaScriptDateObject;
    },
    set: function (value) {
        throw "The property 'JavaScriptDateObject' is read only.";
    }
});

Object.defineProperty(NDateTime.prototype, "TimeOfDay", {
    get: function () {
        return new NTimeSpan(0, this._hour, this._minute, this._second, this._millisecond);
    },
    set: function (value) {
        throw "The property 'TimeOfDay' is read only.";
    }
});

//Methods
//-------------------------------------------------
NDateTime.prototype.AddDays = function (value) {
    var retVal = new Date(this.jsdateObject);
    retVal.setDate(retVal.getDate() + value);
    return new NDateTimeFromJsObject(retVal);
};


NDateTime.prototype.AddHours = function (value) {
    var retVal = new Date(this.jsdateObject);
    retVal.setHours(retVal.getHours() + value);
    return new NDateTimeFromJsObject(retVal);
};

NDateTime.prototype.AddMilliseconds = function (value) {
    var retVal = new Date(this.jsdateObject);
    retVal.setMilliseconds(retVal.getMilliseconds() + value);
    return new NDateTimeFromJsObject(retVal);
};

NDateTime.prototype.AddMinutes = function (value) {
    var retVal = new Date(this.jsdateObject);
    retVal.setMinutes(retVal.getMinutes() + value);
    return new NDateTimeFromJsObject(retVal);
};

NDateTime.prototype.AddMonths = function (value) {
    var retVal = new Date(this.jsdateObject);
    retVal.setMonth(retVal.getMonth() + value);
    return new NDateTimeFromJsObject(retVal);
};

NDateTime.prototype.AddSeconds = function (value) {
    var retVal = new Date(this.jsdateObject);
    retVal.setSeconds(retVal.getSeconds() + value);
    return new NDateTimeFromJsObject(retVal);
};

NDateTime.prototype.AddYears = function (value) {
    var retVal = new Date(this.jsdateObject);
    retVal.setFullYear(retVal.getFullYear() + value);
    return new NDateTimeFromJsObject(retVal);
};

NDateTime.prototype.CompareTo = function (valueToCompare) {
    if (this.jsdateObject == "Invalid Date" || valueToCompare.jsdateObject == "Invalid Date")
        throw "Invalid dates";
    if (this.jsdateObject < valueToCompare.jsdateObject)
        return -1;
    else if (this.jsdateObject > valueToCompare.jsdateObject)
        return 1;
    else
        return 0;
};

NDateTime.prototype.IsLeapYear = function () {
    var yr = this.jsdateObject.getFullYear();
    if ((parseInt(yr) % 4) == 0) {
        if (parseInt(yr) % 100 == 0) {
            if (parseInt(yr) % 400 != 0) {
                return false;
            }
            if (parseInt(yr) % 400 == 0) {
                return true;
            }
        }
        if (parseInt(yr) % 100 != 0) {
            return true;
        }
    }
    if ((parseInt(yr) % 4) != 0) {
        return false;
    }
};

NDateTime.prototype.DaysInMonth = function () {
    if (this.jsdateObject.getMonth() == 0 || this.jsdateObject.getMonth() == 2
        || this.jsdateObject.getMonth() == 4 || this.jsdateObject.getMonth() == 6
        || this.jsdateObject.getMonth() == 7 || this.jsdateObject.getMonth() == 9
        || this.jsdateObject.getMonth() == 11)
        return 31;
    else if (this.jsdateObject.getMonth() == 3 || this.jsdateObject.getMonth() == 5
        || this.jsdateObject.getMonth() == 8 || this.jsdateObject.getMonth() == 10)
        return 30;
    else {
        if (this.IsLeapYear())
            return 29;
        else
            return 28;
    }
    return new Date(this.jsdateObject.getFullYear(), this.jsdateObject.getMonth()).getDate();
};

// Converts the value of the current NDateTime object to its equivalent string representation using the specified format.
// format : custom format string (example: "dd-MM-yyyy" or "dd/MM-yyyy")
// supported format specifier are :
//      "d"    : The day of the month, from 1 through 31.
//      "dd"   : The day of the month, from 01 through 31.
//      "M"    : The month, from 1 through 12.
//      "MM"   : The month, from 01 through 12.
//      "yyyy" : The year as a four-digit number.
NDateTime.prototype.ToString = function (format) {
    if (format == undefined)
        return this.jsdateObject.toString();

    var twoDigitsDay = this.Day;
    if (twoDigitsDay < 10)
        twoDigitsDay = "0" + twoDigitsDay;
    var result = format.replace("dd", twoDigitsDay);
    result = result.replace("d", this.Day);

    var twoDigitsMonth = this.Month;
    if (twoDigitsMonth < 10)
        twoDigitsMonth = "0" + twoDigitsMonth;
    result = result.replace("MM", twoDigitsMonth);
    result = result.replace("M", this.Month);

    result = result.replace("yyyy", this.Year);

    var twoDigitsHour = this.Hour;
    if (twoDigitsHour < 10)
        twoDigitsHour = "0" + twoDigitsHour;
    result = result.replace("HH", twoDigitsHour);
    result = result.replace("H", this.Hour);

    var twoDigitsMinutes = this.Minute;
    if (twoDigitsMinutes < 10)
        twoDigitsMinutes = "0" + twoDigitsMinutes;
    result = result.replace("mm", twoDigitsMinutes);
    result = result.replace("m", this.Minute);

    var twoDigitsSeconds = this.Second;
    if (twoDigitsSeconds < 10)
        twoDigitsSeconds = "0" + twoDigitsSeconds;
    result = result.replace("ss", twoDigitsSeconds);
    result = result.replace("s", this.Second);

    var threeDigitsMilliseconds = this.Millisecond;
    if (threeDigitsMilliseconds.toString().length == 0)
        threeDigitsMilliseconds = "000";
    else if (threeDigitsMilliseconds.toString().length == 1)
        threeDigitsMilliseconds += "00";
    else if (threeDigitsMilliseconds.toString().length == 2)
        threeDigitsMilliseconds += "0";
    result = result.replace("fff", threeDigitsMilliseconds);
    result = result.replace("ff", threeDigitsMilliseconds.toString().substring(0, 2));
    result = result.replace("f", threeDigitsMilliseconds.toString().substring(0, 1));

    return result;
};

//Static methods
//-------------------------------------------------

// Gets the current date
NDateTime.Now = function () {
    return new NDateTimeFromJsObject(new Date());
}

NDateTime.StringDeleteAtPosition = function (value, index, length) {
    return value.substr(0, index) + value.substr(index + length, value.length - 1);
}

// Gets the current date with the time component set to 00:00:00
NDateTime.Today = function () {
    var today = NDateTime.Now();
    today.jsdateObject.setHours(0, 0, 0, 0);
    today.JavaScriptDateObject.setHours(0, 0, 0, 0);
    today._hour = 0;
    today._minute = 0;
    today._second = 0;
    today._millisecond = 0;
    today.Time = "00:00:00";
    return today;
}

// Converts the specified string representation of a date and time to its NDateTime equivalent using the specified format.
// format : custom format string (example: "dd-MM-yyyy" or "dd/MM-yyyy")
// supported format specifier are :
//      "d"    : The day of the month, from 1 through 31.
//      "dd"   : The day of the month, from 01 through 31.
//      "M"    : The month, from 1 through 12.
//      "MM"   : The month, from 01 through 12.
//      "yyyy" : The year as a four-digit number.
NDateTime.Parse = function (value, format) {

    var getPatternPosition = function(value, format) 
    {
        var patternItemsPosition = new Array();
        var patternToCheck = ["yyyy", "MM", "M", "dd", "d", "HH", "H", "mm", "m", "ss", "s"];

        for(var indexPattern=0;indexPattern<patternToCheck.length;indexPattern++)
        {
            var index = format.search(patternToCheck[indexPattern]);
            if (index != -1) {
                var itemTemp = new Object();
                itemTemp.Pattern = patternToCheck[indexPattern];
                itemTemp.Index = index;
                itemTemp.RealSizeAdjustment = 0;
                patternItemsPosition.push(itemTemp);
                var strReplace = "";
                for (var indexChar = 0; indexChar < patternToCheck[indexPattern].length; indexChar++) { strReplace += " " };
                format = format.replace(patternToCheck[indexPattern], strReplace);
            }
        }

        function compare(a, b) {
            if (a.Index < b.Index)
                return -1;
            if (a.Index > b.Index)
                return 1;
            return 0;
        }

        patternItemsPosition = patternItemsPosition.sort(compare);

        return patternItemsPosition;
    }

    var getAdjustment = function (patternItemsPosition, pattern) {
        var retVal = 0;
        for (var indexPattern = 0; indexPattern < patternItemsPosition.length; indexPattern++) {
            if (patternItemsPosition[indexPattern].Pattern == pattern)
                break;
            else
                retVal += patternItemsPosition[indexPattern].RealSizeAdjustment;
        }

        return retVal;
    }

    var year, month, day, hour, minute, second;
    var regexPattern = format;
    var originalValue = value;

    var regex = new RegExp();
    var oneOrTwoDecimalPattern = new RegExp("\\d{1,2}");

    var patternItemsPosition = getPatternPosition(value, format);

    for (var indexPattern = 0; indexPattern < patternItemsPosition.length;indexPattern++)
    {
        if(patternItemsPosition[indexPattern].Pattern=="yyyy") {
            regexPattern = regexPattern.replace("yyyy", "\d{4}");
            year = value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "yyyy"), 4);
        }
        else if(patternItemsPosition[indexPattern].Pattern=="MM"){
            regexPattern = regexPattern.replace("MM", "\\d{2}");
            month = value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "MM"), 2);
        }
        else if(patternItemsPosition[indexPattern].Pattern=="M"){
            regexPattern = regexPattern.replace("M", "\\d{1,2}");
            month = oneOrTwoDecimalPattern.exec(value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "M"), 2))[0];
            patternItemsPosition[indexPattern].RealSizeAdjustment = month.length == 1 ? 0 : month.length - 1;
        }
        else if(patternItemsPosition[indexPattern].Pattern=="dd"){
            regexPattern = regexPattern.replace("dd", "\\d{2}");
            day = value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "dd"), 2);
        }
        else if(patternItemsPosition[indexPattern].Pattern=="d"){
            regexPattern = regexPattern.replace("d", "\\d{1,2}");
            day = oneOrTwoDecimalPattern.exec(value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "d"), 2))[0];
            patternItemsPosition[indexPattern].RealSizeAdjustment = day.length == 1 ? 0 : day.length - 1;
        }
        else if (patternItemsPosition[indexPattern].Pattern == "HH") {
            regexPattern = regexPattern.replace("HH", "\\d{2}");
            hour = value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "HH"), 2);
        }
        else if (patternItemsPosition[indexPattern].Pattern == "H") {
            regexPattern = regexPattern.replace("H", "\\d{1,2}");
            hour = oneOrTwoDecimalPattern.exec(value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "H"), 2))[0];
            patternItemsPosition[indexPattern].RealSizeAdjustment = hour.length == 1 ? 0 : hour.length - 1;
        }
        else if (patternItemsPosition[indexPattern].Pattern == "mm") {
            regexPattern = regexPattern.replace("mm", "\\d{2}");
            minute = value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "mm"), 2);
        }
        else if (patternItemsPosition[indexPattern].Pattern == "m") {
            regexPattern = regexPattern.replace("m", "\\d{1,2}");
            minute = oneOrTwoDecimalPattern.exec(value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "m"), 2))[0];
            patternItemsPosition[indexPattern].RealSizeAdjustment = minute.length == 1 ? 0 : minute.length - 1;
        }
        else if (patternItemsPosition[indexPattern].Pattern == "ss") {
            regexPattern = regexPattern.replace("ss", "\\d{2}");
            second = value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "ss"), 2);
        }
        else if (patternItemsPosition[indexPattern].Pattern == "s") {
            regexPattern = regexPattern.replace("s", "\\d{1,2}");
            second = oneOrTwoDecimalPattern.exec(value.substr(patternItemsPosition[indexPattern].Index + getAdjustment(patternItemsPosition, "s"), 2))[0];
            patternItemsPosition[indexPattern].RealSizeAdjustment = second.length == 1 ? 0 : second.length - 1;
        }
    }

    return new NDateTime(year, month, day, hour, minute, second);
}


//TimeSpan structure
function NTimeSpan(days, hours, minutes, seconds, milliseconds) {
    if (days!=null && days!=undefined)
        this._days = days;
    else
        this._days = 0;

    if (hours != null && hours != undefined)
        this._hours = hours;
    else
        this._hours = 0;

    if (minutes != null && minutes != undefined)
        this._minutes = minutes;
    else
        this._minutes = 0;

    if (seconds != null && seconds != undefined)
        this._seconds = seconds;
    else
        this._seconds = 0;

    if (milliseconds != null && milliseconds != undefined)
        this._milliseconds = milliseconds;
    else
        this._milliseconds = 0;
}

//Properties
//-------------------------------------------------
Object.defineProperty(NTimeSpan.prototype, "Days", {
    get: function () {
        return this._days;
    },
    set: function (value) {
        throw "The property 'Days' is read only.";
    }
});

Object.defineProperty(NTimeSpan.prototype, "Hours", {
    get: function () {
        return this._hours;
    },
    set: function (value) {
        throw "The property 'Hours' is read only.";
    }
});

Object.defineProperty(NTimeSpan.prototype, "Minutes", {
    get: function () {
        return this._minutes;
    },
    set: function (value) {
        throw "The property 'Minutes' is read only.";
    }
});

Object.defineProperty(NTimeSpan.prototype, "Seconds", {
    get: function () {
        return this._seconds;
    },
    set: function (value) {
        throw "The property 'Seconds' is read only.";
    }
});

Object.defineProperty(NTimeSpan.prototype, "Milliseconds", {
    get: function () {
        return this._milliseconds;
    },
    set: function (value) {
        throw "The property 'Milliseconds' is read only.";
    }
});